package com.example.aspect;

import com.example.annotation.DecryptRequestBody;
import com.example.annotation.EncryptResponseBody;
import com.example.config.EncryptionConfig;
import com.example.dto.Request;
import com.example.util.AesUtil;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@Aspect
@Component
public class EncryptionAspect {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private EncryptionConfig encryptionConfig;

    @Around("@annotation(decryptRequestBody) || @within(decryptRequestBody)")
    public Object handleDecryption(ProceedingJoinPoint joinPoint, DecryptRequestBody decryptRequestBody) throws Throwable {
        HttpServletRequest request = getCurrentHttpRequest();

        // 如果请求需要解密，并且配置开启了解密
        if (request != null && encryptionConfig.isDecryptionEnabled()) {
            String encryptedBody = getRequestBody(request);

            // 检查请求体是否加密
            if (AesUtil.isBase64(encryptedBody)) {
                // 解密请求体
                String decryptedBody = AesUtil.decrypt(encryptionConfig.getKey(), encryptionConfig.getInitVector(), encryptedBody);

                // 解析解密后的请求体并替换为原方法的参数
                Object[] args = joinPoint.getArgs();
                Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
                Type[] genericParameterTypes = method.getGenericParameterTypes();

                for (int i = 0; i < args.length; i++) {
                    if (args[i] instanceof Request) {
                        // 获取泛型类型并反序列化
                        if (genericParameterTypes[i] instanceof ParameterizedType) {
                            ParameterizedType parameterizedType = (ParameterizedType) genericParameterTypes[i];
                            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                            JavaType javaType = objectMapper.getTypeFactory().constructParametricType((Class<?>) parameterizedType.getRawType(), (Class<?>) actualTypeArguments[0]);
                            args[i] = objectMapper.readValue(decryptedBody, javaType);
                        }
                    }
                }
                // 执行原方法，传入解密后的参数
                return joinPoint.proceed(args);
            }
        }

        // 如果请求体未加密或不需要解密，直接执行原方法
        return joinPoint.proceed();
    }

    @Around("@annotation(encryptResponseBody) || @within(encryptResponseBody)")
    public Object handleEncryption(ProceedingJoinPoint joinPoint, EncryptResponseBody encryptResponseBody) throws Throwable {
        Object result = joinPoint.proceed();

        if (encryptionConfig.isEncryptionEnabled()) {
            HttpServletResponse response = getCurrentHttpResponse();
            if (response != null) {
                String jsonResponse = objectMapper.writeValueAsString(result);
                String encryptedResponse = AesUtil.encrypt(encryptionConfig.getKey(), encryptionConfig.getInitVector(), jsonResponse);
                writeResponse(response, encryptedResponse);
                return null; // Prevent further processing of the response
            }
        }

        return result;
    }

    private HttpServletRequest getCurrentHttpRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    private HttpServletResponse getCurrentHttpResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }

    private String getRequestBody(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = request.getReader();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
        }
        return stringBuilder.toString();
    }

    private void writeResponse(HttpServletResponse response, String encryptedResponse) throws IOException {
        response.setContentType("text/plain");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(encryptedResponse);
        out.flush();
    }
}
